Micron Document
NexusPi Git Node


Displaying Raw • Download

flash.py 760e92f186fb1467b5e2f8dbf13d568c31979a60 (760e92f1) Text, 16.11 KB

T8b949e#!/usr/bin/env python3
T8b949e"""
RNodeTHV4 Flash Utility

Flash the RNodeTHV4 boundary node firmware to a Heltec WiFi LoRa 32 V4.
No PlatformIO required — just Python 3 and a USB cable.

Usage:
# Flash a pre-built merged binary (from GitHub Releases or local build)
python flash.py

# Flash a specific file
python flash.py --file rnodethv4_firmware.bin

# Download latest from GitHub and flash
python flash.py --download

# Specify serial port manually
python flash.py --port /dev/ttyACM0

# Just build the merged binary (requires PlatformIO build output)
python flash.py --merge-only
"""

Tff7b72import T7ee787argparse
Tff7b72import T7ee787glob
Tff7b72import T7ee787hashlib
Tff7b72import T7ee787os
Tff7b72import T7ee787platform
Tff7b72import T7ee787shutil
Tff7b72import T7ee787subprocess
Tff7b72import T7ee787sys
Tff7b72import T7ee787time

T8b949e# ── Configuration ──────────────────────────────────────────────────────────────

Te6edf3CHIP Tff7b72= Ta5d6ff"Ta5d6ffesp32s3Ta5d6ff"
Te6edf3FLASH_MODE Tff7b72= Ta5d6ff"Ta5d6ffqioTa5d6ff"
Te6edf3FLASH_FREQ Tff7b72= Ta5d6ff"Ta5d6ff80mTa5d6ff"
Te6edf3FLASH_SIZE Tff7b72= Ta5d6ff"Ta5d6ff16MBTa5d6ff"
Te6edf3BAUD_RATE Tff7b72= Ta5d6ff"Ta5d6ff921600Ta5d6ff"
Te6edf3MERGED_FILENAME Tff7b72= Ta5d6ff"Ta5d6ffrnodethv4_firmware.binTa5d6ff"
Te6edf3GITHUB_REPO Tff7b72= Ta5d6ff"Ta5d6ffjrl290/RNodeTHV4Ta5d6ff"

T8b949e# Flash addresses for ESP32-S3 Arduino framework
Te6edf3BOOTLOADER_ADDR Tff7b72= T79c0ff0x0000
Te6edf3PARTITIONS_ADDR Tff7b72= T79c0ff0x8000
Te6edf3BOOT_APP0_ADDR Tff7b72= T79c0ff0xe000
Te6edf3APP_ADDR Tff7b72= T79c0ff0x10000

T8b949e# PlatformIO build output paths (relative to project root)
Te6edf3BUILD_DIR Tff7b72= Ta5d6ff"Ta5d6ff.pio/build/heltec_V4_boundaryTa5d6ff"
Te6edf3BOOTLOADER_BIN Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3BUILD_DIRTb4b4b4, Ta5d6ff"Ta5d6ffbootloader.binTa5d6ff"Tb4b4b4)
Te6edf3PARTITIONS_BIN Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3BUILD_DIRTb4b4b4, Ta5d6ff"Ta5d6ffpartitions.binTa5d6ff"Tb4b4b4)
Te6edf3FIRMWARE_BIN Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3BUILD_DIRTb4b4b4, Ta5d6ff"Ta5d6ffrnode_firmware_heltec32v4_boundary.binTa5d6ff"Tb4b4b4)
Te6edf3BOOT_APP0_BIN Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexpanduserTb4b4b4(
Ta5d6ff"Ta5d6ff~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.binTa5d6ff"
Tb4b4b4)

T8b949e# ── Helpers ────────────────────────────────────────────────────────────────────

Tff7b72def Td2a8fffind_esptoolTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e"""Find esptool.py — pip-installed, bundled, or PlatformIO's copy.

Prefer pip-installed esptool first (handles its own deps), then fall
back to the bundled script — but only if pyserial is importable in
the current Python interpreter.
"""
T8b949e# 1. pip-installed esptool (standalone executable, no dep issues)
Tff7b72if Te6edf3shutilTff7b72.Td2a8ffwhichTb4b4b4(Ta5d6ff"Ta5d6ffesptool.pyTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Tb4b4b4[Ta5d6ff"Ta5d6ffesptool.pyTa5d6ff"Tb4b4b4]
Tff7b72if Te6edf3shutilTff7b72.Td2a8ffwhichTb4b4b4(Ta5d6ff"Ta5d6ffesptoolTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Tb4b4b4[Ta5d6ff"Ta5d6ffesptoolTa5d6ff"Tb4b4b4]

T8b949e# Check if pyserial is available before using script-based esptool
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787serial T8b949e# noqa: F401
Te6edf3has_pyserial Tff7b72= Tff7b72True
Tff7b72except Tf85149ImportErrorTb4b4b4:
Te6edf3has_pyserial Tff7b72= Tff7b72False

T8b949e# 2. Bundled in Release/
Te6edf3bundled Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffReleaseTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffesptoolTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffesptool.pyTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3bundledTb4b4b4) Tff7b72and Te6edf3has_pyserialTb4b4b4:
Tff7b72return Tb4b4b4[Te6edf3sysTff7b72.Td2a8ffexecutableTb4b4b4, Te6edf3bundledTb4b4b4]

T8b949e# 3. PlatformIO's esptool
Te6edf3pio_esptool Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexpanduserTb4b4b4(
Ta5d6ff"Ta5d6ff~/.platformio/packages/tool-esptoolpy/esptool.pyTa5d6ff"
Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3pio_esptoolTb4b4b4) Tff7b72and Te6edf3has_pyserialTb4b4b4:
Tff7b72return Tb4b4b4[Te6edf3sysTff7b72.Td2a8ffexecutableTb4b4b4, Te6edf3pio_esptoolTb4b4b4]

T8b949e# 4. Bundled exists but pyserial is missing — tell the user
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3bundledTb4b4b4) Tff7b72and Tff7b72not Te6edf3has_pyserialTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffFound bundled esptool but pyserial is not installed.Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffInstall it with: pip install pyserialTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffOr install the standalone esptool: pip install esptoolTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)

Tff7b72return Tff7b72None


Tff7b72def Td2a8fffind_serial_portTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e"""List available serial ports and let the user choose."""
Te6edf3system Tff7b72= Te6edf3platformTff7b72.Td2a8ffsystemTb4b4b4(Tb4b4b4)

T8b949e# Gather ports from glob patterns
Tff7b72if Te6edf3system Tff7b72== Ta5d6ff"Ta5d6ffDarwinTa5d6ff"Tb4b4b4:
Te6edf3patterns Tff7b72= Tb4b4b4[Ta5d6ff"Ta5d6ff/dev/cu.usbmodem*Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/dev/tty.usbmodem*Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff/dev/cu.usbserial*Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/dev/cu.SLAB*Ta5d6ff"Tb4b4b4]
Tff7b72elif Te6edf3system Tff7b72== Ta5d6ff"Ta5d6ffLinuxTa5d6ff"Tb4b4b4:
Te6edf3patterns Tff7b72= Tb4b4b4[Ta5d6ff"Ta5d6ff/dev/ttyACM*Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/dev/ttyUSB*Ta5d6ff"Tb4b4b4]
Tff7b72elseTb4b4b4:
Te6edf3patterns Tff7b72= Tb4b4b4[Tb4b4b4]

Te6edf3ports Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3pattern Tff7b72in Te6edf3patternsTb4b4b4:
Te6edf3portsTff7b72.Td2a8ffextendTb4b4b4(Te6edf3globTff7b72.Td2a8ffglobTb4b4b4(Te6edf3patternTb4b4b4)Tb4b4b4)

T8b949e# Also try pyserial's port enumeration (works on all platforms including Windows)
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787serialT7ee787.T7ee787toolsT7ee787.T7ee787list_ports
Tff7b72for Te6edf3port Tff7b72in Te6edf3serialTff7b72.Td2a8fftoolsTff7b72.Td2a8fflist_portsTff7b72.Td2a8ffcomportsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3portTff7b72.Td2a8ffdevice Tff7b72not Tff7b72in Te6edf3portsTb4b4b4:
Te6edf3portsTff7b72.Td2a8ffappendTb4b4b4(Te6edf3portTff7b72.Td2a8ffdeviceTb4b4b4)
Tff7b72except Tf85149ImportErrorTb4b4b4:
Tff7b72pass

T8b949e# Sort for consistent ordering
Te6edf3portsTff7b72.Td2a8ffsortTb4b4b4(Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3portsTb4b4b4:
Tff7b72return Tff7b72None

Tffa657printTb4b4b4(Ta5d6ff"Tffea00\nTa5d6ffAvailable serial ports:Ta5d6ff"Tb4b4b4)
Tff7b72for Te6edf3iTb4b4b4, Te6edf3p Tff7b72in Tffa657enumerateTb4b4b4(Te6edf3portsTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff [Tffd700{Te6edf3iTff7b72+T79c0ff1Tffd700}Ta5d6ff] Tffd700{Te6edf3pTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4)

Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3choice Tff7b72= Tffa657inputTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffSelect port [1-Tffd700{Tffa657lenTb4b4b4(Te6edf3portsTb4b4b4)Tffd700}Ta5d6ff]: Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3idx Tff7b72= Tffa657intTb4b4b4(Te6edf3choiceTb4b4b4) Tff7b72- T79c0ff1
Tff7b72if T79c0ff0 Tff7b72<Tff7b72= Te6edf3idx Tff7b72< Tffa657lenTb4b4b4(Te6edf3portsTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3portsTb4b4b4[Te6edf3idxTb4b4b4]
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149EOFErrorTb4b4b4)Tb4b4b4:
Tff7b72pass
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffInvalid selection, try again.Ta5d6ff"Tb4b4b4)


Tff7b72def Td2a8ffsha256_fileTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
T8b949e"""Compute SHA-256 hash of a file."""
Te6edf3h Tff7b72= Te6edf3hashlibTff7b72.Td2a8ffsha256Tb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Te6edf3pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Tff7b72for Te6edf3chunk Tff7b72in Tffa657iterTb4b4b4(Tff7b72lambdaTb4b4b4: Te6edf3fTff7b72.Td2a8ffreadTb4b4b4(T79c0ff65536Tb4b4b4)Tb4b4b4, Ta5d6ffbTa5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3hTff7b72.Td2a8ffupdateTb4b4b4(Te6edf3chunkTb4b4b4)
Tff7b72return Te6edf3hTff7b72.Td2a8ffhexdigestTb4b4b4(Tb4b4b4)


Tff7b72def Td2a8ffdownload_firmwareTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4:
T8b949e"""Download the latest merged firmware from GitHub Releases."""
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787urllibT7ee787.T7ee787request Tff7b72import Te6edf3urlretrieveTb4b4b4, Te6edf3urlopen
Tff7b72import T7ee787json
Tff7b72except Tf85149ImportErrorTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffError: Python urllib not available.Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False

Te6edf3api_url Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffhttps://api.github.com/repos/Tffd700{Te6edf3GITHUB_REPOTffd700}Ta5d6ff/releases/latestTa5d6ff"
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffChecking latest release from Tffd700{Te6edf3GITHUB_REPOTffd700}Ta5d6ff...Ta5d6ff"Tb4b4b4)

Tff7b72tryTb4b4b4:
Tff7b72with Te6edf3urlopenTb4b4b4(Te6edf3api_urlTb4b4b4) Tff7b72as Te6edf3respTb4b4b4:
Te6edf3release Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadsTb4b4b4(Te6edf3respTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError fetching release info: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False

T8b949e# Find the merged firmware asset
Te6edf3asset_url Tff7b72= Tff7b72None
Tff7b72for Te6edf3asset Tff7b72in Te6edf3releaseTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffassetsTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3assetTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3MERGED_FILENAMETb4b4b4:
Te6edf3asset_url Tff7b72= Te6edf3assetTb4b4b4[Ta5d6ff"Ta5d6ffbrowser_download_urlTa5d6ff"Tb4b4b4]
Tff7b72break

Tff7b72if Tff7b72not Te6edf3asset_urlTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError: Ta5d6ff'Tffd700{Te6edf3MERGED_FILENAMETffd700}Ta5d6ff'Ta5d6ff not found in latest release (Tffd700{Te6edf3releaseTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6fftag_nameTa5d6ff'Tb4b4b4, Ta5d6ff'Ta5d6ff?Ta5d6ff'Tb4b4b4)Tffd700}Ta5d6ff).Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffAvailable assets:Ta5d6ff"Tb4b4b4, Tb4b4b4[Te6edf3aTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4] Tff7b72for Te6edf3a Tff7b72in Te6edf3releaseTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffassetsTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)Tb4b4b4]Tb4b4b4)
Tff7b72return Tff7b72False

Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDownloading Tffd700{Te6edf3releaseTb4b4b4[Ta5d6ff'Ta5d6fftag_nameTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff / Tffd700{Te6edf3MERGED_FILENAMETffd700}Ta5d6ff...Ta5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3urlretrieveTb4b4b4(Te6edf3asset_urlTb4b4b4, Te6edf3dest_pathTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDownload failed: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False

Te6edf3size Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3dest_pathTb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDownloaded Tffd700{Te6edf3sizeTffd700:Ta5d6ff,Tffd700}Ta5d6ff bytes SHA-256: Tffd700{Te6edf3sha256_fileTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff16Tb4b4b4]Tffd700}Ta5d6ff...Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72True


Tff7b72def Td2a8ffmerge_firmwareTb4b4b4(Te6edf3output_pathTb4b4b4, Te6edf3esptool_cmdTb4b4b4)Tb4b4b4:
T8b949e"""Merge bootloader + partitions + boot_app0 + app into a single binary."""
T8b949e# Check all required files exist
Te6edf3required Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffbootloaderTa5d6ff"Tb4b4b4: Te6edf3BOOTLOADER_BINTb4b4b4,
Ta5d6ff"Ta5d6ffpartitionsTa5d6ff"Tb4b4b4: Te6edf3PARTITIONS_BINTb4b4b4,
Ta5d6ff"Ta5d6fffirmwareTa5d6ff"Tb4b4b4: Te6edf3FIRMWARE_BINTb4b4b4,
Tb4b4b4}

T8b949e# boot_app0 can come from PlatformIO or be bundled
Te6edf3boot_app0 Tff7b72= Te6edf3BOOT_APP0_BIN
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3boot_app0Tb4b4b4)Tb4b4b4:
T8b949e# Check if bundled in Release/
Te6edf3alt Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffReleaseTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffboot_app0.binTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3altTb4b4b4)Tb4b4b4:
Te6edf3boot_app0 Tff7b72= Te6edf3alt
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError: boot_app0.bin not found at Tffd700{Te6edf3BOOT_APP0_BINTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff Run Ta5d6ff'Ta5d6ffpio run -e heltec_V4_boundaryTa5d6ff'Ta5d6ff first, or install PlatformIO.Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False
Te6edf3requiredTb4b4b4[Ta5d6ff"Ta5d6ffboot_app0Ta5d6ff"Tb4b4b4] Tff7b72= Te6edf3boot_app0

Tff7b72for Te6edf3nameTb4b4b4, Te6edf3path Tff7b72in Te6edf3requiredTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError: Tffd700{Te6edf3nameTffd700}Ta5d6ff not found: Tffd700{Te6edf3pathTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffRun Ta5d6ff'Ta5d6ffpio run -e heltec_V4_boundaryTa5d6ff'Ta5d6ff to build first.Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False

Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffMerging firmware components...Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Bootloader: Tffd700{Te6edf3BOOTLOADER_BINTffd700}Ta5d6ff @ 0xTffd700{Te6edf3BOOTLOADER_ADDRTffd700:Ta5d6ff04xTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Partitions: Tffd700{Te6edf3PARTITIONS_BINTffd700}Ta5d6ff @ 0xTffd700{Te6edf3PARTITIONS_ADDRTffd700:Ta5d6ff04xTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff boot_app0: Tffd700{Te6edf3boot_app0Tffd700}Ta5d6ff @ 0xTffd700{Te6edf3BOOT_APP0_ADDRTffd700:Ta5d6ff04xTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Firmware: Tffd700{Te6edf3FIRMWARE_BINTffd700}Ta5d6ff @ 0xTffd700{Te6edf3APP_ADDRTffd700:Ta5d6ff05xTffd700}Ta5d6ff"Tb4b4b4)

Te6edf3cmd Tff7b72= Te6edf3esptool_cmd Tff7b72+ Tb4b4b4[
Ta5d6ff"Ta5d6ff--chipTa5d6ff"Tb4b4b4, Te6edf3CHIPTb4b4b4,
Ta5d6ff"Ta5d6ffmerge_binTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--flash_modeTa5d6ff"Tb4b4b4, Te6edf3FLASH_MODETb4b4b4,
Ta5d6ff"Ta5d6ff--flash_freqTa5d6ff"Tb4b4b4, Te6edf3FLASH_FREQTb4b4b4,
Ta5d6ff"Ta5d6ff--flash_sizeTa5d6ff"Tb4b4b4, Te6edf3FLASH_SIZETb4b4b4,
Ta5d6ff"Ta5d6ff-oTa5d6ff"Tb4b4b4, Te6edf3output_pathTb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3BOOTLOADER_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"Tb4b4b4, Te6edf3BOOTLOADER_BINTb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3PARTITIONS_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"Tb4b4b4, Te6edf3PARTITIONS_BINTb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3BOOT_APP0_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"Tb4b4b4, Te6edf3boot_app0Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3APP_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"Tb4b4b4, Te6edf3FIRMWARE_BINTb4b4b4,
Tb4b4b4]

Te6edf3result Tff7b72= Te6edf3subprocessTff7b72.Td2a8ffrunTb4b4b4(Te6edf3cmdTb4b4b4, Te6edf3capture_outputTff7b72=Tff7b72TrueTb4b4b4, Te6edf3textTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72if Te6edf3resultTff7b72.Td2a8ffreturncode Tff7b72!= T79c0ff0Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError merging: Tffd700{Te6edf3resultTff7b72.Td2a8ffstderrTffd700}Tffd700{Te6edf3resultTff7b72.Td2a8ffstdoutTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False

Te6edf3size Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3output_pathTb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Tffea00\nTa5d6ffMerged binary: Tffd700{Te6edf3output_pathTffd700}Ta5d6ff (Tffd700{Te6edf3sizeTffd700:Ta5d6ff,Tffd700}Ta5d6ff bytes)Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffSHA-256: Tffd700{Te6edf3sha256_fileTb4b4b4(Te6edf3output_pathTb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff16Tb4b4b4]Tffd700}Ta5d6ff...Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72True


Tff7b72def Td2a8ffflash_firmwareTb4b4b4(Te6edf3firmware_pathTb4b4b4, Te6edf3portTb4b4b4, Te6edf3esptool_cmdTb4b4b4, Te6edf3baudTff7b72=Te6edf3BAUD_RATETb4b4b4)Tb4b4b4:
T8b949e"""Flash firmware to the device."""
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Tffea00\nTa5d6ffFlashing Tffd700{Te6edf3firmware_pathTffd700}Ta5d6ff to Tffd700{Te6edf3portTffd700}Ta5d6ff...Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Chip: Tffd700{Te6edf3CHIPTffd700}Ta5d6ff Baud: Tffd700{Te6edf3baudTffd700}Ta5d6ff Flash: Tffd700{Te6edf3FLASH_SIZETffd700}Tffea00\nTa5d6ff"Tb4b4b4)

T8b949e# Determine if this is a merged binary (flash at 0x0) or app-only (flash at 0x10000)
T8b949e# The app-only .bin for this project is ~800KB. The merged binary adds
T8b949e# bootloader+partitions+boot_app0 padding (~64KB) so it's slightly larger
T8b949e# but still well under 2MB. A true app-only binary won't have the bootloader
T8b949e# signature at offset 0. Check for the ESP32 bootloader magic byte (0xE9).
Te6edf3size Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3firmware_pathTb4b4b4)
Te6edf3is_merged Tff7b72= Tff7b72False
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3firmware_pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3magic Tff7b72= Te6edf3fTff7b72.Td2a8ffreadTb4b4b4(T79c0ff1Tb4b4b4)
Tff7b72if Te6edf3magic Tff7b72== Ta5d6ffbTa5d6ff'Tffea00\xe9Ta5d6ff'Tb4b4b4:
T8b949e# Has bootloader magic — this is a merged binary starting at 0x0
Te6edf3is_merged Tff7b72= Tff7b72True
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

Tff7b72if Te6edf3is_mergedTb4b4b4:
Te6edf3flash_addr Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3BOOTLOADER_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Detected: merged binary (bootloader magic 0xE9) → flash at Tffd700{Te6edf3flash_addrTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3flash_addr Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ff0xTffd700{Te6edf3APP_ADDRTffd700:Ta5d6ffxTffd700}Ta5d6ff"
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ff Detected: app-only binary → flash at Tffd700{Te6edf3flash_addrTffd700}Ta5d6ff"Tb4b4b4)

Te6edf3cmd Tff7b72= Te6edf3esptool_cmd Tff7b72+ Tb4b4b4[
Ta5d6ff"Ta5d6ff--chipTa5d6ff"Tb4b4b4, Te6edf3CHIPTb4b4b4,
Ta5d6ff"Ta5d6ff--portTa5d6ff"Tb4b4b4, Te6edf3portTb4b4b4,
Ta5d6ff"Ta5d6ff--baudTa5d6ff"Tb4b4b4, Te6edf3baudTb4b4b4,
Ta5d6ff"Ta5d6ff--beforeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffdefault_resetTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--afterTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhard_resetTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffwrite_flashTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff-zTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--flash_modeTa5d6ff"Tb4b4b4, Te6edf3FLASH_MODETb4b4b4,
Ta5d6ff"Ta5d6ff--flash_freqTa5d6ff"Tb4b4b4, Te6edf3FLASH_FREQTb4b4b4,
Ta5d6ff"Ta5d6ff--flash_sizeTa5d6ff"Tb4b4b4, Te6edf3FLASH_SIZETb4b4b4,
Te6edf3flash_addrTb4b4b4, Te6edf3firmware_pathTb4b4b4,
Tb4b4b4]

Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffRunning: Ta5d6ff" Tff7b72+ Ta5d6ff"Ta5d6ff Ta5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3cmdTb4b4b4[Tff7b72-T79c0ff8Tb4b4b4:Tb4b4b4]Tb4b4b4)Tb4b4b4)
Te6edf3result Tff7b72= Te6edf3subprocessTff7b72.Td2a8ffrunTb4b4b4(Te6edf3cmdTb4b4b4)
Tff7b72return Te6edf3resultTff7b72.Td2a8ffreturncode Tff7b72== T79c0ff0


T8b949e# ── Main ───────────────────────────────────────────────────────────────────────

Tff7b72def Td2a8ffmainTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(
Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffRNodeTHV4 Flash Utility — flash boundary node firmware to Heltec V4Ta5d6ff"Tb4b4b4,
Te6edf3formatter_classTff7b72=Te6edf3argparseTff7b72.Td2a8ffRawDescriptionHelpFormatterTb4b4b4,
Te6edf3epilogTff7b72=Ta5d6ff"""
Ta5d6ffExamples:
Ta5d6ff python flash.py # Flash local merged binary
Ta5d6ff python flash.py --download # Download latest release and flash
Ta5d6ff python flash.py --file firmware.bin # Flash a specific file
Ta5d6ff python flash.py --merge-only # Build merged binary from PlatformIO output
Ta5d6ff python flash.py --port /dev/ttyACM0 # Specify serial port
Ta5d6ff Ta5d6ff"""Tb4b4b4,
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--fileTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-fTa5d6ff"Tb4b4b4, Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffPath to firmware binary to flashTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--portTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-pTa5d6ff"Tb4b4b4, Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffSerial port (auto-detected if omitted)Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--baudTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-bTa5d6ff"Tb4b4b4, Te6edf3defaultTff7b72=Te6edf3BAUD_RATETb4b4b4, Te6edf3helpTff7b72=Ta5d6fffTa5d6ff"Ta5d6ffBaud rate (default: Tffd700{Te6edf3BAUD_RATETffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--downloadTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-dTa5d6ff"Tb4b4b4, Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffDownload latest firmware from GitHub ReleasesTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--merge-onlyTa5d6ff"Tb4b4b4, Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffMerge PlatformIO build output into single binary, donTa5d6ff'Ta5d6fft flashTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--no-mergeTa5d6ff"Tb4b4b4, Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffSkip merge step, use existing merged binary or --fileTa5d6ff"Tb4b4b4)

Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Te6edf3baud Tff7b72= Te6edf3argsTff7b72.Td2a8ffbaud

Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff╔══════════════════════════════════════════╗Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ RNodeTHV4 Flash Utility ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ Heltec WiFi LoRa 32 V4 Boundary Node ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff╚══════════════════════════════════════════╝Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4)

T8b949e# Find esptool
Te6edf3esptool_cmd Tff7b72= Te6edf3find_esptoolTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3esptool_cmdTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffError: esptool not found!Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffInstall it with: pip install esptoolTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffUsing esptool: Tffd700{Ta5d6ff'Ta5d6ff Ta5d6ff'Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3esptool_cmdTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)

T8b949e# Determine firmware file
Te6edf3firmware_path Tff7b72= Tff7b72None

Tff7b72if Te6edf3argsTff7b72.Td2a8fffileTb4b4b4:
Te6edf3firmware_path Tff7b72= Te6edf3argsTff7b72.Td2a8fffile
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3firmware_pathTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError: file not found: Tffd700{Te6edf3firmware_pathTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)

Tff7b72elif Te6edf3argsTff7b72.Td2a8ffdownloadTb4b4b4:
Te6edf3firmware_path Tff7b72= Te6edf3MERGED_FILENAME
Tff7b72if Tff7b72not Te6edf3download_firmwareTb4b4b4(Te6edf3firmware_pathTb4b4b4)Tb4b4b4:
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)

Tff7b72elif Te6edf3argsTff7b72.Td2a8ffmerge_onlyTb4b4b4:
Tff7b72if Te6edf3merge_firmwareTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4, Te6edf3esptool_cmdTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Tffea00\nTa5d6ffDone! Flash with: python flash.py --file Tffd700{Te6edf3MERGED_FILENAMETffd700}Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)
Tff7b72return

Tff7b72elseTb4b4b4:
T8b949e# Try to find or create a merged binary
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4) Tff7b72and Tff7b72not Te6edf3argsTff7b72.Td2a8ffno_mergeTb4b4b4:
T8b949e# Check if PlatformIO build is newer
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3FIRMWARE_BINTb4b4b4)Tb4b4b4:
Te6edf3build_time Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetmtimeTb4b4b4(Te6edf3FIRMWARE_BINTb4b4b4)
Te6edf3merge_time Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetmtimeTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4)
Tff7b72if Te6edf3build_time Tff7b72> Te6edf3merge_timeTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffBuild output is newer than merged binary, re-merging...Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3merge_firmwareTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4, Te6edf3esptool_cmdTb4b4b4)Tb4b4b4:
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)
Te6edf3firmware_path Tff7b72= Te6edf3MERGED_FILENAME
Tff7b72elif Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3FIRMWARE_BINTb4b4b4)Tb4b4b4:
T8b949e# Build exists but no merged binary — create one
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffFound PlatformIO build output, creating merged binary...Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3merge_firmwareTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4, Te6edf3esptool_cmdTb4b4b4)Tb4b4b4:
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)
Te6edf3firmware_path Tff7b72= Te6edf3MERGED_FILENAME
Tff7b72elif Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3MERGED_FILENAMETb4b4b4)Tb4b4b4:
Te6edf3firmware_path Tff7b72= Te6edf3MERGED_FILENAME
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffNo firmware found!Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffOptions:Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff 1. Build with PlatformIO first: pio run -e heltec_V4_boundaryTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff 2. Download from GitHub: python flash.py --downloadTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff 3. Specify a file: python flash.py --file <path>Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)

T8b949e# Flash
Te6edf3port Tff7b72= Te6edf3argsTff7b72.Td2a8ffport Tff7b72or Te6edf3find_serial_portTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3portTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\nTa5d6ffError: No serial port detected!Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffConnect your Heltec V4 via USB and try again,Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffor specify manually: python flash.py --port /dev/ttyACM0Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)

Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Tffea00\nTa5d6ffSerial port: Tffd700{Te6edf3portTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFirmware: Tffd700{Te6edf3firmware_pathTffd700}Ta5d6ff (Tffd700{Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3firmware_pathTb4b4b4)Tffd700:Ta5d6ff,Tffd700}Ta5d6ff bytes)Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4)

Te6edf3confirm Tff7b72= Tffa657inputTb4b4b4(Ta5d6ff"Ta5d6ffFlash firmware? [Y/n] Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3confirm Tff7b72and Te6edf3confirm Tff7b72!= Ta5d6ff"Ta5d6ffyTa5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffAborted.Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)

Tff7b72if Te6edf3flash_firmwareTb4b4b4(Te6edf3firmware_pathTb4b4b4, Te6edf3portTb4b4b4, Te6edf3esptool_cmdTb4b4b4, Te6edf3baudTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff╔══════════════════════════════════════════╗Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ Flash complete! ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ Device will reboot automatically. ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ On first boot, hold PRG > 5s to enter ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff║ the configuration portal. ║Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff╚══════════════════════════════════════════╝Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\nTa5d6ffFlash FAILED. Check connection and try again.Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffYou may need to hold BOOT while pressing RESET.Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff1Tb4b4b4)


Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3mainTb4b4b4(Tb4b4b4)

Served by rngit 1.5.0 - Generated in 0.04s